
#include <stdio.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>

#include "Config.h"
#include "EesTraderApi.h"
#include "Event.h"
#include "Lock.h"
#include "mylogger.h"
#include "mystruct.h"
#include "qtapi.h"
#include "string.h"

EESTraderApi *CreateEESTraderApi(void);

extern vector<IPolicy *> *g_policy;

class CTradeSpi;

class CFClient;

#define MAX_ORDERS_ 100
#define TRADES_COUNT_ 100
#define MSG_FINSTRUMENT_SATATUS2_ 2

class BaseInsPosition {
 public:
  bool m_bIsSHFE;
  bool m_bIsCZCE;
  char m_cIns[32];
  char m_cTradingDay[16];

  int LongVolume;
  int LongTodayVolume;
  int LongTotalClosable;
  int LongTodayClosable;
  double LongOpenAmmount;
  double LongPositionAmmount;
  double LongCloseProfit;
  double LongPositionProfit;
  int ShortVolume;
  int ShortTodayVolume;
  int ShortTotalClosable;
  int ShortTodayClosable;
  double ShortOpenAmmount;
  double ShortPositionAmmount;
  double ShortCloseProfit;
  double ShortPositionProfit;

  int m_nMultiply;

 public:
  BaseInsPosition() {
    LongVolume = 0;
    LongTodayVolume = 0;
    LongTotalClosable = 0;
    LongTodayClosable = 0;
    LongOpenAmmount = 0;
    LongPositionAmmount = 0;
    LongCloseProfit = 0;

    ShortVolume = 0;
    ShortTodayVolume = 0;
    ShortTotalClosable = 0;
    ShortTodayClosable = 0;
    ShortOpenAmmount = 0;
    ShortPositionAmmount = 0;
    ShortCloseProfit = 0;
    m_nMultiply = 1;

    LongCloseProfit = 0;
    LongPositionProfit = 0;

    ShortCloseProfit = 0;
    ShortPositionProfit = 0;
  }

  virtual ~BaseInsPosition() {}

  virtual void Lock() = 0;

  virtual void Unlock() = 0;
};

struct FroznPos {
  // int orderId;
  EES_SideType side;
  int frozen_num;
  FroznPos() {
    // orderId = 0;
    frozen_num = 0;
  }
};

struct RemPostion {
  int short_pre;                        // 昨日空头持仓
  int short_today;                      // 今日空头持仓
  int long_pre;                         // 昨日多头持仓
  int long_today;                       // 今日多头持仓
  unordered_map<int, FroznPos> frozen;  // client_token  --> frozen num
  RemPostion() {
    short_pre = 0;
    short_today = 0;
    long_pre = 0;
    long_today = 0;
  }
  void print() {
    int num = 0;
    for (auto ele : frozen) {
      num = num + ele.second.frozen_num;
      printf("  side %d", ele.second.side);
    }
    printf("\n");
    printf(
        " RemPostion  Debug short_pre %d   short_today %d   long_pre %d   "
        "long_today %d  frozen_num %d \n",
        short_pre, short_today, long_pre, long_today, num);
  }
};
struct RemOrderInfo {
  EES_SideType side;
  string symbol;
};

class RemInsPosition : public BaseInsPosition {
 public:
  double OpenCommissionRateByVolume;
  double OpenCommissionRateByMoney;
  double CloseCommissionRateByMoney;
  double CloseCommissionRateByVolume;
  double CloseTodayCommissionRateByMoney;
  double CloseTodayCommissionRateByVolume;

  double Commission;
  double ClosedAmmount;
  double ClosedTodayAmmount;
  double OpenedAmmount;

  int OpenedVolume;
  int ClosedVolume;
  int ClosedTodayVolume;

  double LongMarginRateByMoney;
  double ShortMarginRateByMoney;

  double LongMarginByVolume;
  double ShortMarginByVolume;

 private:
  CLock m_Lock;
  list<PositionDetailField *> m_lTdLong;
  list<PositionDetailField *> m_lYdLong;

  list<PositionDetailField *> m_lTdShort;
  list<PositionDetailField *> m_lYdShort;

 private:
  void AddPositionDetail(list<PositionDetailField *> *pList, int nVolume,
                         double fOpenPrice, double fPositionPrice,
                         const char *pszOpenDate, const char *pszTradeID,
                         bool bNewOpen = false);

  void CloseLongPositionDetail(list<PositionDetailField *> *pList,
                               EES_OrderExecutionField *pField);

  void CloseShortPositionDetail(list<PositionDetailField *> *pList,
                                EES_OrderExecutionField *pField);

 public:
  RemInsPosition(const char *pszIns, bool bIsSHFE, const char *pszTradingDay,
                 int nMultiply);

  virtual ~RemInsPosition();

  inline void UpdatePosition(EES_OrderExecutionField *pField);

  inline void AddPositionDetail(EES_AccountPosition *pPosDetail);

  inline void AddPositionDetail(EES_OrderExecutionField *pField, bool bIsLong);

  inline void ClosePositionDetail(EES_OrderExecutionField *pField);

  inline void GetLongPos(int &nTotal, int &nClosable, int &nTodayClosable) {
    nTotal = LongVolume;
    nClosable = LongTotalClosable;
    nTodayClosable = LongTodayClosable;
  }

  inline void GetLongPos(int &nTotal, int &nClosable, int &nTodayClosable,
                         double &fAvgOpenPrice) {
    nTotal = LongVolume;
    nClosable = LongTotalClosable;
    nTodayClosable = LongTodayClosable;
    if (LongVolume > 0) {
      fAvgOpenPrice = LongOpenAmmount / LongVolume;
    } else {
      fAvgOpenPrice = 0;
    }
  }

  inline void GetShortPos(int &nTotal, int &nClosable, int &nTodayClosable) {
    nTotal = ShortVolume;
    nClosable = ShortTotalClosable;
    nTodayClosable = ShortTodayClosable;
  }

  inline void GetShortPos(int &nTotal, int &nClosable, int &nTodayClosable,
                          double &fAvgOpenPrice) {
    nTotal = ShortVolume;
    nClosable = ShortTotalClosable;
    nTodayClosable = ShortTodayClosable;
    if (ShortVolume > 0) {
      fAvgOpenPrice = ShortOpenAmmount / ShortVolume;
    } else {
      fAvgOpenPrice = 0;
    }
  }

  inline void GetPos(int &nLong, int &nLongClosable, int nLongTodayClosable,
                     int &nShort, int &nShortClosable,
                     int nShortTodayClosable) {
    nLong = LongVolume;
    nLongClosable = LongTotalClosable;
    nLongTodayClosable = LongTodayClosable;

    nShort = ShortVolume;
    nShortClosable = ShortTotalClosable;
    nShortTodayClosable = ShortTodayClosable;
  }

  // todo
  void CalProfit() {}

  // todo
  void CalCommission() {}

  void Lock() { m_Lock.Lock(); }

  void Unlock() { m_Lock.Unlock(); }
};

struct RemOrderField : public OrderField {
  char UserOrderLocalID[21];
  char BrokerID[8];
  char UserID[12];
  char InvestorID[12];

  bool Frozened;
  bool Restored;
  int TradedVolume;

  time_t CancelTime;
  bool SysReturned;
  bool CtpReturned;
  int Stage1;
  int Stage2;
  int Stage3;
  int Canceling;

  EES_EnterOrderField *pInputOrderField;

  RemOrderField() { memset(this, 0, sizeof(RemOrderField)); }
};

class CTradeSpi : public EESTraderEvent {
 public:
  // CThostFtdcBrokerTradingParamsField	m_Params;
  map<string, EES_SymbolField *> m_Instruments;
  map<string, RemInsPosition *> m_Positions;
  EES_AccountInfo m_Account;
  EES_AccountBP m_account_bp;
  CLock m_AcctLock;

  void *m_pFutureAccount;

  time_t m_tLastTradeTime;
  int m_bOnline;
  CMyConfig2 m_Config;

  // update 2023
  unordered_map<string, RemPostion> m_ees_pos;
  unordered_map<int, RemOrderInfo> m_orderid_side;
  unordered_map<string, InstrumentField> m_symbol_info;
  string acct_id;
  unordered_map<string, int> m_inst_cancel_num;
  // attributes;
 public:
  int GetCancelingNum(const char *Instrument);

 private:
  bool m_bInBg;
  int m_nLastError;
  char m_cLastError[256];
  char m_cInitFile[256];
  int m_nSeconds;
  char m_cCurTime[12];

  time_t m_tLastOrderTime;
  time_t m_tLastQueryAcctTime;

  EESTraderApi *m_pTradeApi;

  CSpinLock m_PosLock;
  CSpinLock m_OrderLock;

  int m_nOrdersCount;
  int m_nOrdersCreated;
  RemOrderField *m_pOrders[MAX_ORDERS_ + 1];
  map<string, RemOrderField *> m_OrdersByLocalID;
  map<string, RemOrderField *> m_OrdersBySysID;

  int m_nNextTrade;
  TradeField m_TradeFields[TRADES_COUNT_];

  map<string, string> m_LatestTradeID;
  set<string> m_TradeIDs;

  map<string, list<EES_OrderExecutionField *> *> m_CachedTrades;

  CLock m_OALock;
  map<int, int> m_FailedOA;

  // market data cache;
  CLock m_MdLock;
  map<string, MarketDataField *> m_LastMarketData;

  // log buffers;
  // LogMsg	m_LogMsg[256];
  int m_nNextMsg;
  CLock m_lockMsg;

  char m_cInvestorID[32];
  int m_nFrontID;
  int m_nSessionID;

  CEvent m_hQueryFinished;
  list<CFClient *> m_Clients;
  map<string, IPolicy *> policies;              // <OrderRef, Policy>
  map<string, RemOrderField *> RemOrderFields;  // <OrderRef, RemOrderField>
  CLock m_ClientsLock;

  CSpinLock m_coclock;
  map<string, int> m_CancelOrdersCount;

  FILE *m_pfLog;
  int m_nSeed;

  // my variable
  bool m_connected;
  bool b_authenticated;
  bool b_position;
  bool b_instruments;
  // 新加3个参数
  //  std::string pszAppID;
  //  std::string pszAuthCode;
  //  std::string pszProductInfo;
  // 新增持仓
  std::unordered_map<std::string, EES_AccountPosition> long_pos_map_;
  std::unordered_map<std::string, EES_AccountPosition> short_pos_map_;
  // 品种信息
  std::unordered_map<std::string, EES_SymbolField> future_instruments_;

  vector<IPolicy *> v_policy;

 private:
  void CacheTrade(EES_OrderExecutionField *pField);

  void FlushCachedTrade(RemOrderField *pOrder);

  RemOrderField *GetOrderByBrokerOID(const char *pszBrokerOrderID);

  double GetLastPrice(const char *psz) {
    double fPrice = 0;
    m_MdLock.Lock();
    map<string, MarketDataField *>::iterator it = m_LastMarketData.find(psz);
    if (it != m_LastMarketData.end()) {
      MarketDataField *pData = it->second;
      fPrice = pData->LastPrice;
    }
    m_MdLock.Unlock();
    return fPrice;
  }

  double GetAveragePrice(const char *psz) {
    double fPrice = 0;
    m_MdLock.Lock();
    map<string, MarketDataField *>::iterator it = m_LastMarketData.find(psz);
    if (it != m_LastMarketData.end()) {
      MarketDataField *pData = it->second;
      fPrice = pData->AveragePrice;
    }
    m_MdLock.Unlock();
    return fPrice;
  }

  double GetPreSettlementPrice(const char *psz) {
    double fPrice = 0;
    m_MdLock.Lock();
    map<string, MarketDataField *>::iterator it = m_LastMarketData.find(psz);
    if (it != m_LastMarketData.end()) {
      MarketDataField *pData = it->second;
      fPrice = pData->PreSettlementPrice;
    }
    m_MdLock.Unlock();
    return fPrice;
  }

  // query instruments;
  int QueryAccount();

  // query instruments;
  // int QueryInstruments();

  // query detail positions;
  // int QueryDetailPositions();

  int ReqClientLogin(const char *pszAuthCode);

  int ReqUserLogin();

  bool req_position();
  bool req_settlement_confirm();
  bool req_qry_instrument();

  void ReqUserInvestor();

  // confirm settlement;
  // int Confirm();

  // update local position cache;
  inline void UpdatePosition(EES_OrderExecutionField *pField);

  // check whether an instrument is of SHFE exchange;
  inline bool IsSHFE(const char *pszIns);

  // frozen position when a close order is sent;
  void FrozenPosition(RemOrderField *pOrder, const char *pszCode, char cBuySell,
                      char cClose, int nVolume);

  // unfrozend position when a close order is canceled;
  void UnfrozenPosition(RemOrderField *pOrder);

  // get next trade field;
  TradeField *GetNextTradeField();

 public:
  CTradeSpi(const char *pszInitFile = nullptr);
  virtual ~CTradeSpi();

  void AddPolicy(IPolicy *newPolicy) { v_policy.push_back(newPolicy); }

  void DeletePolicy(IPolicy *oldPolicy) {
    vector<IPolicy *>::iterator iter = v_policy.begin();
    for (; iter != v_policy.end(); iter++) {
      if (*iter == oldPolicy) break;
    }

    v_policy.erase(iter);
  }

  bool CancelOrder(RemOrderField *pOrder) {
    EES_CancelOrder temp;
    memset(&temp, 0, sizeof(EES_CancelOrder));

    strcpy(temp.m_Account, m_Config.GetInvestorID());
    temp.m_Quantity = pOrder->Volume;
    temp.m_MarketOrderToken = pOrder->OrderID;

    RESULT ret = m_pTradeApi->CancelOrder(&temp);
    if (ret != NO_ERROR) {
      SPDLOG_ERROR("send cancel failed {}", ret);
      return false;
    }

    SPDLOG_INFO("send cancel successfully");
    return true;
  };

  int GetCancelOrdersCount(const char *pszIns) {
    int nCount = 0;
    m_coclock.Lock();
    map<string, int>::iterator it = m_CancelOrdersCount.find(pszIns);
    if (it != m_CancelOrdersCount.end()) {
      nCount = it->second;
    }
    m_coclock.Unlock();

    return nCount;
  }

  vector<IPolicy *> *Get_policy() { return &v_policy; }
  void IncCancelOrdersCount(const char *pszIns) {
    m_coclock.Lock();
    map<string, int>::iterator it = m_CancelOrdersCount.find(pszIns);
    if (it != m_CancelOrdersCount.end()) {
      it->second++;
    } else {
      m_CancelOrdersCount.insert(pair<string, int>(pszIns, 1));
    }
    m_coclock.Unlock();
  }
  void PostLogMessage(const char *pszLog, int nLevel);
  void OpenLog() {
    char cFileName[256];
    char cDate[16];

    time_t t;
    time(&t);

    struct tm *pTm = localtime(&t);
    strftime(cDate, 15, "%Y%m%d", pTm);

    strcpy(cFileName, "./logs/");
    strcat(cFileName, m_Config.GetUser());
    strcat(cFileName, "_");
    strcat(cFileName, cDate);
    strcat(cFileName, ".log");

    m_pfLog = fopen(cFileName, "at+");
  }

  /**
   *login to trade;
   */
  virtual int Login(const char *UserID, const char *Password, char *ErrorMsg,
                    int *ErrorID);

  virtual int Logout() {
    if (m_pTradeApi) {
      SPDLOG_INFO(" call logout");
      int nTried = 0;
      // int retCode = m_pTradeApi->ReqUserLogout(&reqUserLogout, 2);
      int retCode = m_pTradeApi->DisConnServer();

      while (retCode < 0 && nTried++ < 4) {
        std::cout << "in Logout while " << std::endl;
        sleep(1);
        retCode = m_pTradeApi->DisConnServer();
      }
    }
    return 1;
  }

  virtual IFutureTrader *CreateTrader(IPolicy *pPolicy, char cHedgeFlag = '1',
                                      bool IsSHFE = false);

  void Stop() {
    if (m_pTradeApi != NULL) {
      SPDLOG_INFO(" call stop");
      // m_pTradeApi->RegisterSpi(NULL);
      m_pTradeApi->DisConnServer();
      m_pTradeApi = NULL;
    }
  }

  virtual void Release() { delete this; }

  int GetLastErrorID();

  char *GetLastErrorMsg();

  void Log(const char *pszLog, int nLevel, long ThreadID);

  void CloseLog();

  virtual void Debug(const char *pszLog);

  virtual void Info(const char *pszLog);

  virtual void Error(const char *pszLog);

  // LogMsg* GetLogMsg();

  virtual void OnFutureMarketData(MarketDataField *pData);

  const char *GetInitFile();

  const char *GetTradingDay() { return this->m_Config.GetTradingDay(); }
  // create emtpy new orders for late use;
  void CreateEmptyOrders();

  bool IsFinished(RemOrderField *pOrder);

  RemOrderField *GetOrderByID(int nOrderID);

  RemOrderField *GetNewOrder();

  RemOrderField *GetNewOrderNoLock();

  RemOrderField *GetNewOrderByID(int nOrderID);

  int CheckMyOrderID(const char *psz);

  // login to the md server;
  int Login(const char *pszUserID, const char *pszPassword);

  // set TimeCondition = '1' only for FAK
  // set TimeCondition = '1' & VolCondition = '3' for FOK
  inline RemOrderField *Buy(const char *pszCode, int nVolume, double fPrice,
                            IPolicy *pOrderListener = NULL,
                            char HedgeFlag = EES_HedgeFlag_Speculation,
                            char cPriceType = '2', char cTimeCondition = '3',
                            char cVolCondition = '1', int nMinVolume = 0) {
    return PlaceOrder(pszCode, EES_PosiDirection_long, EES_SideType_open_long,
                      nVolume, fPrice, pOrderListener, HedgeFlag, cPriceType,
                      cTimeCondition, cVolCondition, nMinVolume);
  }

  inline RemOrderField *Short(const char *pszCode, int nVolume, double fPrice,
                              IPolicy *pOrderListener = NULL,
                              char HedgeFlag = EES_HedgeFlag_Speculation,
                              char cPriceType = '2', char cTimeCondition = '3',
                              char cVolCondition = '1', int nMinVolume = 0) {
    return PlaceOrder(pszCode, EES_PosiDirection_short, EES_SideType_open_short,
                      nVolume, fPrice, pOrderListener, HedgeFlag, cPriceType,
                      cTimeCondition, cVolCondition, nMinVolume);
  }

  inline RemOrderField *Sell(const char *pszCode, int nVolume, double fPrice,
                             bool bIsSHFE = false,
                             IPolicy *pOrderListener = NULL,
                             char HedgeFlag = EES_HedgeFlag_Speculation,
                             char cPriceType = '2', char cTimeCondition = '3',
                             char cVolCondition = '1', int nMinVolume = 0) {
    return PlaceOrder(pszCode, EES_PosiDirection_short, EES_SideType_close_long,
                      nVolume, fPrice, pOrderListener, HedgeFlag, cPriceType,
                      cTimeCondition, cVolCondition, nMinVolume);
  }

  inline RemOrderField *Cover(const char *pszCode, int nVolume, double fPrice,
                              bool bIsSHFE = false,
                              IPolicy *pOrderListener = NULL,
                              char HedgeFlag = EES_HedgeFlag_Speculation,
                              char cPriceType = '2', char cTimeCondition = '3',
                              char cVolCondition = '1', int nMinVolume = 0) {
    return PlaceOrder(pszCode, EES_PosiDirection_long, EES_SideType_close_short,
                      nVolume, fPrice, pOrderListener, HedgeFlag, cPriceType,
                      cTimeCondition, cVolCondition, nMinVolume);
  }

  inline RemOrderField *PlaceOrder(
      const char *pszCode, char cBuySell, char cOpenClose, int nVolume,
      double fPrice, IPolicy *pPolicy = NULL,
      char HedgeFlag = EES_HedgeFlag_Speculation, char cPriceType = '2',
      char cTimeCondition = '3', char cVolCondition = '1', int nMinVolume = 0) {
    int nRetCode;

    struct tm *t;
    time_t tt;
    time(&tt);
    t = localtime(&tt);
    // if (t->tm_mday > 29 || t->tm_mon != 10 -1) {
    if (t->tm_mon != 1 - 1) {
      SPDLOG_ERROR("TIME  RANGE OUT ");
      return nullptr;
    }

    EES_EnterOrderField *pInputOrderField = new EES_EnterOrderField();

    strcpy(pInputOrderField->m_Account, m_Config.GetInvestorID());
    strcpy(pInputOrderField->m_Symbol, pszCode);

    m_OrderLock.Lock();

    if (b_instruments) {
      auto iter = future_instruments_.find(std::string(pszCode));
      if (iter != future_instruments_.end()) {
        pInputOrderField->m_Exchange = iter->second.m_ExchangeID;
      } else {
        // do nothing, waiting for  OnRspOrderInsert
      }
    } else {
      // do nothing, waiting for  OnRspOrderInsert
    }

    if (HedgeFlag == '1') {  // 投机
      HedgeFlag = EES_HedgeFlag_Speculation;

    } else if (HedgeFlag == '2') {  // 套利
      HedgeFlag = EES_HedgeFlag_Arbitrage;
    } else if (HedgeFlag == '3') {  // 套利
      HedgeFlag = EES_HedgeFlag_Hedge;
    }

    int nReqID = m_Config.GetNextRequestID();
    long orderRef = m_Config.GetAndIncMaxOrderRef();

    pInputOrderField->m_Tif = EES_OrderTif_Day;
    pInputOrderField->m_HedgeFlag = HedgeFlag;
    pInputOrderField->m_Side = cOpenClose;

    pInputOrderField->m_SecType = EES_SecType_fut;
    pInputOrderField->m_Price = fPrice;
    pInputOrderField->m_Qty = nVolume;
    pInputOrderField->m_ClientOrderToken = orderRef;
    m_orderid_side[pInputOrderField->m_ClientOrderToken].side = cOpenClose;
    m_orderid_side[pInputOrderField->m_ClientOrderToken].symbol =
        string(pInputOrderField->m_Symbol);
    pInputOrderField->m_Exchange = EES_ExchangeID_shfe;

    RemOrderField *pOrder = new RemOrderField();
    memset(pOrder, 0, sizeof(RemOrderField));
    strcpy(pOrder->BrokerID, pInputOrderField->m_Account);
    pOrder->Direction = pInputOrderField->m_Side;
    // to do
    // pOrder->ExchangeID = pInputOrderField->m_Exchange;
    pOrder->pInputOrderField = pInputOrderField;
    strcpy(pOrder->UserID, m_Config.GetUser());
    strcpy(pOrder->InvestorID, m_Config.GetInvestorID());
    strcpy(pOrder->InstrumentID, pszCode);
    pOrder->Volume = nVolume;
    strcpy(pOrder->UserOrderLocalID,
           std::to_string(pInputOrderField->m_ClientOrderToken).c_str());

    policies.insert(make_pair(to_string(orderRef), pPolicy));
    RemOrderFields.insert(make_pair(to_string(orderRef), pOrder));

    if (policies.find(std::to_string(pInputOrderField->m_ClientOrderToken)) !=
            policies.end() &&
        RemOrderFields.find(std::to_string(
            pInputOrderField->m_ClientOrderToken)) != RemOrderFields.end()) {
      SPDLOG_INFO("start to insert order. OrderID = {}",
                  pInputOrderField->m_ClientOrderToken);
    }

    SPDLOG_INFO("start to insert order. acct = {}",
                pInputOrderField->m_Account);
    SPDLOG_INFO("start to insert order. symbol = {}",
                pInputOrderField->m_Symbol);
    SPDLOG_INFO("start to insert order. side = {}", pInputOrderField->m_Side);
    SPDLOG_INFO("start to insert order. px = {}", pInputOrderField->m_Price);
    SPDLOG_INFO("start to insert order. m_Qty = {}", pInputOrderField->m_Qty);
    nRetCode = m_pTradeApi->EnterOrder(pInputOrderField);
    SPDLOG_INFO("PlaceOrder nRetCode: {} ", (nRetCode == 0 ? "OK" : "Fail"));
    SPDLOG_INFO("PlaceOrder nRetCode: {} ", nRetCode);

    m_OrderLock.Unlock();
    // delete pOrder;
    return pOrder;
  }

  void GetLongPos(const char *pszIns, int &nTotal, int &nClosable,
                  int &nTodayClosable);

  void GetShortPos(const char *pszIns, int &nTotal, int &nClosable,
                   int &nTodayClosable);

  void GetLongPos(const char *pszIns, int &nTotal, int &nClosable,
                  int &nTodayClosable, double &fAvgOpenPrice);

  void GetShortPos(const char *pszIns, int &nTotal, int &nClosable,
                   int &nTodayClosable, double &fAvgOpenPrice);

  struct InstrumentField *GetInstrument(const char *pszInstrument);

  bool GetInstrument(FutureInstrumentField *pIns);

  void GetInstrument(const char *pszProductID, FutureInstrumentField **pIns,
                     int *pCount);

  int GetMultiply(const char *pszInstrument);

  struct CThostFtdcRspInstrumentField *GetCtpInstrument(
      const char *pszInstrument);

  void SetBgTaskState(bool bInBg);

  void OnOneSecond();

  void RegisterTimer(RemOrderField *pField, int nAction, int nSecondsAfter,
                     IPolicy *pListener);

  void GetLongAvgPrice(const char *pszIns, double &fAvgOpenPrice,
                       double &fAvgPositionPrice);

  void GetShortAvgPrice(const char *pszIns, double &fAvgOpenPrice,
                        double &fAvgPositionPrice);

  // test
 public:
  void test_order();

  // rem
  // spi----------------------------------------------------------------------------------------------------------------------------------
 public:
  virtual void OnConnection(ERR_NO errNo, const char *pErrStr);

  /// 连接断开消息的回调

  /// \brief	服务器主动断开，会收到这个消息
  /// \param  ERR_NO errNo         连接成功能与否的消息
  /// \param  const char* pErrStr  错误信息
  /// \return void

  virtual void OnDisConnection(ERR_NO errNo, const char *pErrStr);

  /// 登录消息的回调

  /// \param  pLogon                  登录成功或是失败的结构
  /// \return void

  virtual void OnUserLogon(EES_LogonResponse *pLogon);

  /// 修改密码响应回调

  /// \param  nResult                  服务器响应的成功与否返回码
  /// \return void

  virtual void OnRspChangePassword(EES_ChangePasswordResult nResult);

  /// 查询用户下面帐户的返回事件

  /// \param  pAccountInfo	        帐户的信息
  /// \param  bFinish	                如果没有传输完成，这个值是 false
  /// ，如果完成了，那个这个值为 true \remark 如果碰到 bFinish ==
  /// true，那么是传输结束，并且 pAccountInfo值无效。 \return void

  virtual void OnQueryUserAccount(EES_AccountInfo *pAccoutnInfo, bool bFinish);

  /// 查询帐户下面期货仓位信息的返回事件
  /// \param  pAccount	                帐户ID
  /// \param  pAccoutnPosition	        帐户的仓位信息
  /// \param  nReqId		                发送请求消息时候的ID号。
  /// \param  bFinish
  /// 如果没有传输完成，这个值是false，如果完成了，那个这个值为 true \remark
  /// 如果碰到 bFinish == true，那么是传输结束，并且 pAccountInfo值无效。
  /// \return void
  virtual void OnQueryAccountPosition(const char *pAccount,
                                      EES_AccountPosition *pAccoutnPosition,
                                      int nReqId, bool bFinish);

  /// 查询帐户下面期权仓位信息的返回事件, 注意这个回调,
  /// 和上一个OnQueryAccountPosition, 会在一次QueryAccountPosition请求后,
  /// 分别返回, 先返回期货, 再返回期权, 即使没有期权仓位,
  /// 也会返回一条bFinish=true的记录 \param  pAccount	                帐户ID
  /// \param  pAccoutnPosition	        帐户的仓位信息
  /// \param  nReqId		                发送请求消息时候的ID号。
  /// \param  bFinish
  /// 如果没有传输完成，这个值是false，如果完成了，那个这个值为 true \remark
  /// 如果碰到 bFinish == true，那么是传输结束，并且 pAccountInfo值无效。
  /// \return void
  virtual void OnQueryAccountOptionPosition(
      const char *pAccount, EES_AccountOptionPosition *pAccoutnOptionPosition,
      int nReqId, bool bFinish);

  /// 查询帐户下面资金信息的返回事件

  /// \param  pAccount	                帐户ID
  /// \param  pAccoutnPosition	        帐户的仓位信息
  /// \param  nReqId		                发送请求消息时候的ID号
  /// \return void

  virtual void OnQueryAccountBP(const char *pAccount,
                                EES_AccountBP *pAccoutnPosition, int nReqId);

  /// 查询合约列表的返回事件

  /// \param  pSymbol	                    合约信息
  /// \param  bFinish	                    如果没有传输完成，这个值是
  /// false，如果完成了，那个这个值为 true \remark 如果碰到 bFinish ==
  /// true，那么是传输结束，并且 pSymbol 值无效。 \return void

  virtual void OnQuerySymbol(EES_SymbolField *pSymbol, bool bFinish);

  /// 查询帐户交易保证金的返回事件

  /// \param  pAccount                    帐户ID
  /// \param  pSymbolMargin               帐户的保证金信息
  /// \param  bFinish	                    如果没有传输完成，这个值是
  /// false，如果完成，那个这个值为 true \remark 如果碰到 bFinish ==
  /// true，那么是传输结束，并且 pSymbolMargin 值无效。 \return void

  virtual void OnQueryAccountTradeMargin(const char *pAccount,
                                         EES_AccountMargin *pSymbolMargin,
                                         bool bFinish);

  /// 查询帐户交易费用的返回事件

  /// \param  pAccount                    帐户ID
  /// \param  pSymbolFee	                帐户的费率信息
  /// \param  bFinish	                    如果没有传输完成，这个值是
  /// false，如果完成了，那个这个值为 true \remark 如果碰到 bFinish == true
  /// ，那么是传输结束，并且 pSymbolFee 值无效。 \return void

  virtual void OnQueryAccountTradeFee(const char *pAccount,
                                      EES_AccountFee *pSymbolFee, bool bFinish);

  // virtual void OnCxlOrderReject(EES_CxlOrderRej *pReject);  //
  /// 下单被柜台系统接受的事件

  /// \brief 表示这个订单已经被柜台系统正式的接受
  /// \param  pAccept	                    订单被接受以后的消息体
  /// \return void

  virtual void OnOrderAccept(EES_OrderAcceptField *pAccept);

  /// 下单被市场接受的事件

  /// \brief 表示这个订单已经被交易所正式的接受
  /// \param  pAccept 订单被接受以后的消息体，里面包含了市场订单ID \return void
  virtual void OnOrderMarketAccept(EES_OrderMarketAcceptField *pAccept);

  ///	下单被柜台系统拒绝的事件

  /// \brief	订单被柜台系统拒绝，可以查看语法检查或是风控检查。
  /// \param  pReject	                    订单被接受以后的消息体
  /// \return void

  virtual void OnOrderReject(EES_OrderRejectField *pReject);

  ///	下单被市场拒绝的事件

  /// \brief	订单被市场拒绝，可以查看语法检查或是风控检查。
  /// \param  pReject 订单被接受以后的消息体，里面包含了市场订单ID \return void

  virtual void OnOrderMarketReject(EES_OrderMarketRejectField *pReject);

  ///	订单成交的消息事件

  /// \brief	成交里面包括了订单市场ID，建议用这个ID查询对应的订单
  /// \param  pExec 订单被接受以后的消息体，里面包含了市场订单ID \return void

  virtual void OnOrderExecution(EES_OrderExecutionField *pExec);

  ///	订单成功撤销事件

  /// \brief	成交里面包括了订单市场ID，建议用这个ID查询对应的订单
  /// \param  pCxled 订单被接受以后的消息体，里面包含了市场订单ID \return void

  virtual void OnOrderCxled(EES_OrderCxled *pCxled);

  ///	撤单被拒绝的消息事件

  /// \brief	一般会在发送撤单以后，收到这个消息，表示撤单被拒绝
  /// \param  pReject	                   撤单被拒绝消息体
  /// \return void

  virtual void OnCxlOrderReject(EES_CxlOrderRej *pReject);

  ///	查询订单的返回事件

  /// \brief	查询订单信息时候的回调，这里面也可能包含不是当前用户下的订单
  /// \param  pAccount                 帐户ID
  /// \param  pQueryOrder	             查询订单的结构
  /// \param  bFinish	                 如果没有传输完成，这个值是
  /// false，如果完成了，那个这个值为 true \remark 如果碰到 bFinish ==
  /// true，那么是传输结束，并且 pQueryOrder值无效。 \return void

  virtual void OnQueryTradeOrder(const char *pAccount,
                                 EES_QueryAccountOrder *pQueryOrder,
                                 bool bFinish);

  ///	查询订单的返回事件

  /// \brief	查询订单信息时候的回调，这里面也可能包含不是当前用户下的订单成交
  /// \param  pAccount                        帐户ID
  /// \param  pQueryOrderExec	                查询订单成交的结构
  /// \param  bFinish
  /// 如果没有传输完成，这个值是false，如果完成了，那个这个值为 true \remark
  /// 如果碰到 bFinish == true，那么是传输结束，并且pQueryOrderExec值无效。
  /// \return void

  virtual void OnQueryTradeOrderExec(const char *pAccount,
                                     EES_QueryOrderExecution *pQueryOrderExec,
                                     bool bFinish);

  ///	接收外部订单的消息

  /// \brief	一般会在系统订单出错，进行人工调整的时候用到。
  /// \param  pPostOrder	                    查询订单成交的结构
  /// \return void

  virtual void OnPostOrder(EES_PostOrder *pPostOrder);

  ///	接收外部订单成交的消息

  /// \brief	一般会在系统订单出错，进行人工调整的时候用到。
  /// \param  pPostOrderExecution	             查询订单成交的结构
  /// \return void

  virtual void OnPostOrderExecution(
      EES_PostOrderExecution *pPostOrderExecution);

  ///	查询交易所可用连接的响应

  /// \brief	每个当前系统支持的汇报一次，当bFinish=
  /// true时，表示所有交易所的响应都已到达，但本条消息本身不包含有用的信息。
  /// \param  pPostOrderExecution	             查询订单成交的结构
  /// \return void
  virtual void OnQueryMarketSession(EES_ExchangeMarketSession *pMarketSession,
                                    bool bFinish);

  ///	交易所连接状态变化报告，

  /// \brief	当交易所连接发生连接/断开时报告此状态
  /// \param  MarketSessionId: 交易所连接代码
  /// \param  ConnectionGood:
  /// true表示交易所连接正常，false表示交易所连接断开了。 \return void
  virtual void OnMarketSessionStatReport(EES_MarketSessionId MarketSessionId,
                                         bool ConnectionGood);

  ///	合约状态变化报告

  /// \brief	当合约状态发生变化时报告
  /// \param  pSymbolStatus: 参见EES_SymbolStatus合约状态结构体定义
  /// \return void
  virtual void OnSymbolStatusReport(EES_SymbolStatus *pSymbolStatus);

  ///	合约状态查询响应

  /// \brief  响应合约状态查询请求
  /// \param  pSymbolStatus: 参见EES_SymbolStatus合约状态结构体定义
  /// \param	bFinish:
  /// 当为true时，表示查询所有结果返回。此时pSymbolStatus为空指针NULL \return
  /// void
  virtual void OnQuerySymbolStatus(EES_SymbolStatus *pSymbolStatus,
                                   bool bFinish);

  /// 深度行情查询响应
  /// \param	pMarketMBLData: 参见EES_MarketMBLData深度行情结构体定义
  /// \param	bFinish:
  /// 当为true时，表示查询所有结果返回。此时pMarketMBLData内容中,仅m_RequestId有效
  /// \return void
  // virtual void OnQueryMarketMBLData(EES_MarketMBLData* pMarketMBLData, bool
  // bFinish) ;
};

class CFClient : public IFutureTrader {
 private:
  bool m_bIsSHFE;
  char m_cHedgeType;
  CTradeSpi *m_pTradeSpi;

 public:
  void *m_pFutureAcct;
  IPolicy *m_pPolicy;

 public:
  CFClient();
  ;

  CFClient(IPolicy *pPolicy, CTradeSpi *pTradeSpi, bool bIsSHFE,
           char cHedgeType);

  virtual ~CFClient() {
    if (m_pTradeSpi != NULL) m_pTradeSpi->DeletePolicy(m_pPolicy);
  };

  virtual int GetCancelingNum(const char *InstrumentID) {
    return m_pTradeSpi->GetCancelingNum(InstrumentID);
  }

  IFutureAccount *GetFutureAccount() { return (IFutureAccount *)m_pFutureAcct; }
  RemOrderField *Buy(const char *pszCode, int nVolume, double fPrice,
                     char cPriceType = '2', char cTimeCondition = '3',
                     char cVolCondition = '1', int nMinVolume = 0) {
    return m_pTradeSpi->Buy(pszCode, nVolume, fPrice, m_pPolicy, m_cHedgeType,
                            cPriceType, cTimeCondition, cVolCondition,
                            nMinVolume);
  }

  virtual OrderField *Buy(const char *InstrumentID, int nVolume, double fPrice);

  RemOrderField *Sell(const char *pszCode, int nVolume, double fPrice,
                      char cPriceType = '2', char cTimeCondition = '3',
                      char cVolCondition = '1', int nMinVolume = 0) {
    return m_pTradeSpi->Sell(pszCode, nVolume, fPrice, m_bIsSHFE, m_pPolicy,
                             m_cHedgeType, cPriceType, cTimeCondition,
                             cVolCondition, nMinVolume);
  }

  virtual OrderField *Sell(const char *InstrumentID, int nVolume, double fPrice,
                           int closeTd);

  RemOrderField *Short(const char *pszCode, int nVolume, double fPrice,
                       char cPriceType = '2', char cTimeCondition = '3',
                       char cVolCondition = '1', int nMinVolume = 0) {
    return m_pTradeSpi->Short(pszCode, nVolume, fPrice, m_pPolicy, m_cHedgeType,
                              cPriceType, cTimeCondition, cVolCondition,
                              nMinVolume);
  }

  virtual OrderField *Short(const char *InstrumentID, int nVolume,
                            double fPrice);

  RemOrderField *Cover(const char *pszCode, int nVolume, double fPrice,
                       char cPriceType = '2', char cTimeCondition = '3',
                       char cVolCondition = '1', int nMinVolume = 0) {
    return m_pTradeSpi->Cover(pszCode, nVolume, fPrice, m_bIsSHFE, m_pPolicy,
                              m_cHedgeType, cPriceType, cTimeCondition,
                              cVolCondition, nMinVolume);
  }

  virtual OrderField *Cover(const char *InstrumentID, int nVolume,
                            double fPrice, int closeTd);

  bool CancelOrder(OrderField *pOrder) {
    return m_pTradeSpi->CancelOrder((RemOrderField *)pOrder);
    // return true;
  }

  virtual void GetTradingAccountInfo(double *PreBalance, double *CloseProfit,
                                     double *PositionProfit, double *Commission,
                                     double *Balance, double *Available) {
    m_pTradeSpi->m_AcctLock.Lock();
    *PreBalance = m_pTradeSpi->m_account_bp.m_InitialBp;
    *CloseProfit = m_pTradeSpi->m_account_bp.m_TotalLiquidPL;
    *PositionProfit = m_pTradeSpi->m_account_bp.m_TotalMarketPL;
    *Commission = m_pTradeSpi->m_Account.m_CommissionFee;
    *Balance = m_pTradeSpi->m_account_bp.m_FrozenMargin +
               m_pTradeSpi->m_account_bp.m_Margin +
               m_pTradeSpi->m_Account.m_AvailableBp;
    *Available = m_pTradeSpi->m_Account.m_AvailableBp;
    m_pTradeSpi->m_AcctLock.Unlock();
  }

  virtual void GetTradingAccountInfo2(double *PreBalance, double *CloseProfit,
                                      double *PositionProfit, double *Margin,
                                      double *Commission, double *Balance,
                                      double *Available) {
    m_pTradeSpi->m_AcctLock.Lock();
    //*PreBalance =  m_pTradeSpi->m_Account.PreBalance;
    //*CloseProfit =  m_pTradeSpi->m_Account.CloseProfit;
    //*PositionProfit =  m_pTradeSpi->m_Account.PositionProfit;
    //*Margin = m_pTradeSpi->m_Account.ExchangeMargin;
    //*Commission =  m_pTradeSpi->m_Account.Commission;
    //*Balance =  m_pTradeSpi->m_Account.Balance;
    //*Available =  m_pTradeSpi->m_Account.Available;
    m_pTradeSpi->m_AcctLock.Unlock();
  }

  bool GetInstrument(FutureInstrumentField *pIns) {
    return m_pTradeSpi->GetInstrument(pIns);
  }

  void GetInstrument(const char *pszProductID, FutureInstrumentField **ppIns,
                     int *pCount) {
    m_pTradeSpi->GetInstrument(pszProductID, ppIns, pCount);
  }

  void GetLongPos(const char *pszCode, int &nTotal, int &nClosable,
                  int &nTodayClosable) {
    m_pTradeSpi->GetLongPos(pszCode, nTotal, nClosable, nTodayClosable);
  }

  void GetShortPos(const char *pszCode, int &nTotal, int &nClosable,
                   int &nTodayClosable) {
    m_pTradeSpi->GetShortPos(pszCode, nTotal, nClosable, nTodayClosable);
  }

  void GetLongPos(const char *pszCode, int &nTotal, int &nClosable,
                  int &nTodayClosable, double &fAvgOpenPrice) {
    m_pTradeSpi->GetLongPos(pszCode, nTotal, nClosable, nTodayClosable,
                            fAvgOpenPrice);
  }

  void GetShortPos(const char *pszCode, int &nTotal, int &nClosable,
                   int &nTodayClosable, double &fAvgOpenPrice) {
    m_pTradeSpi->GetShortPos(pszCode, nTotal, nClosable, nTodayClosable,
                             fAvgOpenPrice);
  }

  void SendInstrumentStatus(CThostFtdcInstrumentStatusField *pStatus) {
    if (m_pPolicy) {
      CThostFtdcInstrumentStatusField *pNew =
          new CThostFtdcInstrumentStatusField();
      memcpy(pNew, pStatus, sizeof(CThostFtdcInstrumentStatusField));

      m_pPolicy->AddMsg(MSG_FINSTRUMENT_SATATUS2_, (void *)pNew);
    }
  }

  virtual const char *GetTradingDay() { return m_pTradeSpi->GetTradingDay(); }

  void RegisterTimer(int nMilliSecondsAfter, int nAction, OrderField *pOrder);

  void Release();

  void LogError(const char *pszError);

  void LogInfo(const char *pszError);

  void LogDebug(const char *pszError);

  int GetCancelOrdersCount(const char *pszIns) {
    return m_pTradeSpi->GetCancelOrdersCount(pszIns);
  }

  virtual void SetSHFE(bool bSHFE);
};

class REM_EES_AccountInfo : public IFutureAccount {
 private:
  char m_cUser[64];
  char m_cPassword[64];

 public:
  REM_EES_AccountInfo() {
    m_cUser[0] = '\0';
    m_cPassword[0] = '\0';
  }

  CTradeSpi *m_pTradeSpi;

  virtual const char *GetUser();

  const char *GetBrokerID() { return m_pTradeSpi->m_Config.GetBroker(); }

  virtual const char *GetPassword();

  /**
   *login to trade;
   */
  virtual int Login(const char *UserID, const char *Password, char *ErrorMsg,
                    int *ErrorID);

  virtual int Logout();

  virtual IFutureTrader *CreateTrader(IPolicy *pPolicy, char cHedgeFlag = '1',
                                      bool IsSHFE = false);

  virtual void Stop();

  virtual void Release();
};

extern "C" {
IFutureAccount *CreateAccount(const char *pszConfigFile);
}
